/* ----------------------------------------------------------------------
 * File: 		parser.cpp
 * Created by:		Taylor Wood
 * Email:		Tdwood2@asu.edu
 * Student ID:		1202553801
 *
 * Description:
 * This is the parser file. Here the linked list will be dissected and
 * errors will be thrown if no grammar is found.
 * ----------------------------------------------------------------------*/

#include "parser.h"

int error = 0;
std::ofstream fout;

void isProgram(char * test) {
	// first set the current token to the head
	fout.open(test);
	setHead();

	while (getCurrToken()->next != NULL) {

		// next we begin dissecting the linked list
		if (isType(getCurrToken()))
			incrToken();
		else 
			fout << "Line " << getCurrToken()->line << ":\texpected type\n";

		if (isId(getCurrToken()))
			incrToken();
		else
			fout << "Line " << getCurrToken()->line << ":\texpected identifier\n";

		if (isDvarGlobal());
		else if (isDmethod());
	}

	fout.close();

}

int isType(token * tok) {

	if (tok->name == "KEYWORD") 
		if (tok->word == "char" || tok->word == "boolean" || tok->word == "string" || tok->word == "int" || tok->word == "float" || tok->word == "void")
			return 1;
	return 0;

}

int isId(token * tok) {
	if (tok->name == "ID")
		return 1;
	fout << "Line " << tok->line << ":\texpected identifier or value\n";
	errorFix();
	return 0;
}

int isDvarGlobal() {

	while (getCurrToken()->word != ";") {
		if (getCurrToken()->word == ",") 
			incrToken();
		else 
			return 0;
		if (isId(getCurrToken())) 
			incrToken();
		else {
			fout << "Line" << getCurrToken()->line << ":\texpected delimiter\n";
			return 0;
		}
	}

	incrToken();

	return 1;
}

int isDmethod() {
	if (getCurrToken()->word[0] == '(')
		incrToken();
	else {
		fout << "Line " << getCurrToken()->line << ":\texpected delimiter\n";
		return 0;
	}

	if (isParam());
	else if (getCurrToken()->word[0] == ')')
		incrToken();
	else {
		fout << "Line " << getCurrToken()->line << ":\texpected delimiter\n";
		return 0;
	}

	if (getCurrToken()->word[0] == '{')
		incrToken();
	else {
		fout << "Line " << getCurrToken()->line << ":\texpected delimiter\n";
		return 0;
	}

	while (isLine());

	if (error) return 0;

	if (getCurrToken()->word[0] == '}')
		incrToken();
	else {
		fout << "Line " << getCurrToken()->line << ":\texpected delimiter\n";
		return 0;
	}
	return 1;
}

// Based on the image provided on BB
int isDvarLocal() {

	if (isType(getCurrToken()));
	//incrToken();
	else {
		//fout << "Line " << getCurrToken()->line << "\texpected type\n";
		return 0;
	}

	if (isId(getCurrToken()->next)) {
		// weird bug where it won't increment the token unless there are three increments
		incrToken();
		incrToken();
	}
	//incrToken();
	else {
		//fout << "Line " << getCurrToken()->line << "\texpected identifier\n";
		return 0;
	}


	while (getCurrToken()->word != ";") {

		if (getCurrToken()->word == "=") {
			incrToken();
			if (isExprlog());
			if (error) return 0;
			incrToken();
		}
		else if (getCurrToken()->word == ",") {
			incrToken();
			if (isId(getCurrToken()))
				incrToken();
			else {
				fout << "Line " << getCurrToken()->line << "\texpected type\n";
				return 0;
			}
		}
		else {
			fout << "Line " << getCurrToken()->line << "\texpected type or delimiter\n";
			return 0;
		}

	}

	return 1;

}

int isParam() {

	do {

		if (isType(getCurrToken()))
			incrToken();
		else {
			//fout << "Line " << getCurrToken()->line << "\texpected type\n";
			return 0;
		}

		if (isId(getCurrToken()))
			incrToken();
		else {
			fout << "Line " << getCurrToken()->line << "\texpected identifier\n";
			return 0;
		}

		if (getCurrToken()->word == ",")
			incrToken();
		else
			return 1;

	} while (isType(getCurrToken()));

}

int isLine() {

	while (getCurrToken()->word[0] != '}') {
		error = 0;
		if (!error && isDvarLocal())
			return 1;
		else if (!error && isAssign())
			return 1;
		else if (!error && isIf())
			return 1;
		else if (!error && isWhile())
			return 1;
		else if (!error && isReturn())
			return 1;
		else if (!error && isCallFunction())
			return 1;
	}
	return 0;
}

int isIf() {

	if (getCurrToken()->word == "if") 
		incrToken();
	else {
		return 0;
	}

	if (getCurrToken()->word[0] == '(')
		incrToken();
	else {
		fout << "Line " << getCurrToken()->line << ":\texpected delimiter\n";
		errorFix();
		return 0;
	}

	if (isExprlog())
		incrToken();

	else
		return 0;

	if (getCurrToken()->word[0] == ')')
		incrToken();
	else {
		fout << "Line " << getCurrToken()->line << ":\texpected delimiter\n";
		errorFix();
		return 0;
	}

	if (isLine());
	else if (getCurrToken()->word[0] == '{') {
		incrToken();
		while (isLine());

		if (getCurrToken()->word[0] == '}')
			incrToken();
		else {
			return 0;
		}
	}
	else {
		return 0;
	}

	if (isElse())
		return 1;

	return 1;

}

int isElse() {

	if (getCurrToken()->word == "else")
		incrToken();
	else {
		return 0;
	}
	if (isLine())
		return 1;
	else if (getCurrToken()->word[0] == '{') {
		incrToken();
		while (isLine());

		if (getCurrToken()->word[0] == '}')
			incrToken();
		else {
			return 0;
		}
	}
	else {
		return 0;
	}	
}

int isWhile() {

	if (getCurrToken()->word == "while")
		incrToken();
	else
		return 0;

	if (getCurrToken()->word[0] == '(')
		incrToken();
	else {
		return 0;
	}

	if (isExprlog());
	else {
		return 0;
	}

	if (getCurrToken()->word[0] == ')')
		incrToken();
	else {
		return 0;
	}
	if (isLine())
		return 1;
	else if (getCurrToken()->word[0] == '{') {
		incrToken();
		while (isLine());

		if (getCurrToken()->word[0] == '}')
			incrToken();
		else {
			return 0;
		}
	}
	else {
		return 0;
	}	

}

int isAssign() {
	if (getCurrToken()->name == "ID");
	//incrToken();
	else 
		return 0;
	if (getCurrToken()->next->word == "=") {
		incrToken();
		incrToken();
	}
	else {
		return 0;
	}

	if (isExprlog());
	else {
		return 0;
	}

	if (getCurrToken()->word[0] == ';') {
		incrToken();
		return 1;
	}
	else {
		return 0;
	}
}

int isReturn() {
	if (getCurrToken()->word == "return")
		incrToken();
	else 
		return 0;
	if (getCurrToken()->word[0] == ';') {
		incrToken();
		return 1;
	}

	else if (isExprlog());
	else
		return 0;

	if (getCurrToken()->word[0] == ';') {
		incrToken();
		return 1;
	}
	else {
		return 0;
	}
}

int isCallFunction() {
	if (getCurrToken()->name == "ID");
	else {
		return 0;
	}

	if (getCurrToken()->next->word[0] == '(') {
		incrToken();
		incrToken();
	}
	else {
		return 0;
	}

	if (isExprlog()) {
		incrToken();
		while (getCurrToken()->word == ",") {
			incrToken();
			if (isExprlog());
			else {
				return 0;
			}
			incrToken();
		}
	}
	else if (getCurrToken()->word[0] == ')') 
		incrToken();
	else {
		return 0;
	}

	if (error) return 0;

	else {

		if (getCurrToken()->word[0] == ')') 
			incrToken();
		else {
			return 0;
		}

		if (getCurrToken()->word[0] == ';') {
			incrToken();
			return 1;
		}
		else {
			incrToken();
			return 0;
		}
	}

}

int isExprlog() {
	if (isOpand()) {
		while (getCurrToken()->word == "|") {
			incrToken();
			if (!isOpand())
				return 0;
		}
	}	
	else {
		return 0;
	}

	return 1;
}

int isOpand() {
	if (isOpno()) {
		while (getCurrToken()->word == "&") {
			incrToken();
			if (!isOpno())
				return 1;
		}
	}

	if (error) return 0;
	else return 1;
}

int isOpno() {
	if (getCurrToken()->word == "!") {
		incrToken();
		if (isExprel())
			incrToken();
	}
	else if (isExprel()) {
		return 1;
	}
	else {
		return 0;
	}

	return 1;
}

int isExprel() {
	while (isExpr()) {
		if (getCurrToken()->word == ">" || getCurrToken()->word == "<" || getCurrToken()->word == "!=" || getCurrToken()->word == "==" || getCurrToken()->word == ">=" || getCurrToken()->word == "<=") {
			incrToken();
		}
		else {
			return 1;
		}
	}

	if (error) return 0;
	return 1;
}

int isExpr() {
	while (isProduct()) {
		if (getCurrToken()->word == "+" || getCurrToken()->word == "-")
			incrToken();
		else {
			return 1;
		}
	}

	if (error) return 0;
	return 1;
}

int isProduct() {
	while (isSign()) {
		if (getCurrToken()->word == "/" || getCurrToken()->word == "*" || getCurrToken()->word == "%")
			incrToken();
		else {
			return 1;
		}
	}

	if (error) return 0;

	return 1;
}

int isSign() {
	if (getCurrToken()->word == "-")
		incrToken();
	if (isTerm())
		return 1;
	else {
		return 0;
	}
}

int isTerm() {

	token * tok = getCurrToken();
	if (getCurrToken()->word[0] == '(') {
		incrToken();
		if (getCurrToken()->name == "KEYWORD") {
			incrToken();
			return 1;
		}
		else if (isCallFunction());
		else if (tok->name == "INTEGER" || tok->name == "FLOAT" || tok->name == "CHAR" || tok->name == "STRING" || tok->name == "HEXADECIMAL" || tok->name == "OCTAL" || tok->word == "true" || tok->word == "false");
		else if (isId(getCurrToken()))
			incrToken();
		else {
			fout << "Line " << getCurrToken()->line << ":\texpected keyword\n";
			errorFix();
			return 0;
		}

		if (getCurrToken()->word[0] == ')') 
			return 1;
		else {
			fout << "Error\n";
			return 0;
		}
	}
	else {
		if (tok->name == "INTEGER" || tok->name == "FLOAT" || tok->name == "CHAR" || tok->name == "STRING" || tok->name == "HEXADECIMAL" || tok->name == "OCTAL" || tok->word == "true" || tok->word == "false")
			return 1;
		else if (isId(getCurrToken())) {
			incrToken();
			return 1;
		}
		else if (error)
			return 0;
		if (getCurrToken()->name == "KEYWORD") {
			incrToken();
			return 1;
		}
		else if (isCallFunction())
			return 1;
		else {
			fout << "Line " << getCurrToken()->line << ":\texpected identifier or keyword\n"; 
			errorFix();
			return 0;
		}
	}
}

void errorFix() {
	//increment the token until we get to the end of the line
	int i = getCurrToken()->line;
	error = 1;
	while (getCurrToken()->next != NULL && getCurrToken()->line == i)
		incrToken();
}
